﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Profile;
using IProfileDAL;
using ProfileFactory;
using System.Configuration;
using Model;
using System.Collections.Specialized;
using BLL;

namespace Profile
{
    public sealed class iBookShopProfileProvider : ProfileProvider
    {
        //Get an instance of the Profile DAL using the ProfileFactory
        private static readonly IiBookShopProfileProvider dal = DataAccess.CreateiBookShopProfileProvider();

        //Private members
        private const string ERR_INVALID_PARAMETER = "Invalid Profile parameter";
        private const string PROFILE_SHOPPINGCART = "ShoppingCart";
        private static string applicationName = "iBookShop";

        /// <summary>
        /// The name of the appliaction using the custom profiles provider.
        /// </summary>
        public override string ApplicationName
        {
            get
            {
                return applicationName;
            }
            set
            {
                applicationName = value;
            }
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "iBookShop Profile Provider");
            }

            if (string.IsNullOrEmpty(name))
            {
                name = "iBookShopProfileProvider";
            }

            if (config["applicationName"] != null && !string.IsNullOrEmpty(config["applicationName"].Trim()))
            {
                applicationName = config["applicationName"];
            }

            base.Initialize(name, config);
        }

        /// <summary>
        /// Returns the collection of settings property values for the specified application instance and settings property group.
        /// </summary>
        /// <param name="context">A System.Configuration.SettingsContext describing the current application use.</param>
        /// <param name="collection">A System.Configuration.SettingsPropertyCollection containing the settings property group whose values are to be retrieved.</param>
        /// <returns>A System.Configuration.SettingsPropertyValueCollection containing the values for the specified settings property group.</returns>
        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
        {
            string username = (string)context["UserName"];

            SettingsPropertyValueCollection svc = new SettingsPropertyValueCollection();

            foreach (SettingsProperty prop in collection)
            {
                SettingsPropertyValue pv = new SettingsPropertyValue(prop);
                if (pv.Property.Name == PROFILE_SHOPPINGCART)
                {
                    pv.PropertyValue = GetCartItems(username);
                }
                else
                {
                    throw new ApplicationException(ERR_INVALID_PARAMETER + " name.");
                }
                svc.Add(pv);
            }
            return svc;
        }

        /// <summary>
        /// Sets the values of the specified group of property settings.
        /// </summary>
        /// <param name="context">A System.Configuration.SettingsContext describing the current application usage.</param>
        /// <param name="collection">A System.Configuration.SettingsPropertyValueCollection representing the group of property settings to set.</param>
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            string username = (string)context["UserName"];
            CheckUserName(username);
            bool isAuthenticated = (bool)context["IsAuthenticated"];
            int profileId = dal.GetProfileId(username, isAuthenticated, false, applicationName);
            if (profileId == 0)
            {
                profileId = dal.CreateProfileForUser(username, isAuthenticated, ApplicationName);
            }

            foreach (SettingsPropertyValue pv in collection)
            {
                if (pv.Property.Name == PROFILE_SHOPPINGCART)
                {
                    SetCartItems(profileId, (Cart)pv.PropertyValue);
                }
                else
                {
                    throw new ApplicationException(ERR_INVALID_PARAMETER);
                }
            }
            UpdateActivityDates(username);
        }

        //Retrieve the cart
        private Cart GetCartItems(string username)
        {
            Cart cart = new Cart();
            foreach(CartItemInfo cartItem in dal.GetCartItems(username,applicationName))
            {
                cart.Add(cartItem);
            }
            return cart;
        }

        //Update cart
        private void SetCartItems(int profileId, Cart cart)
        {
            dal.SetCartItems(profileId, cart.GetAllCartItems());
        }

        //Update the activity date
        private void UpdateActivityDates(string username)
        {
            dal.UpdateActivityDates(username, applicationName);
        }

        //Delete profiles
        private bool DeleteProfile(string username)
        {
            CheckUserName(username);
            return dal.DeleteProfile(username, applicationName);
        }

        /// <summary>
        /// Deletes profile properties and information for the supplied list of profiles.
        /// </summary>
        /// <param name="profiles">A System.Web.Profile.ProfileInfoCollection of information about profiles that are to be deleted.</param>
        /// <returns>The number of profiles deleted from the data source.</returns>
        public override int DeleteProfiles(ProfileInfoCollection profiles)
        {
            int deleteCount = 0;
            foreach (UserProfileInfo p in profiles)
            {
                if (DeleteProfile(p.Username))
                {
                    deleteCount++;
                }
            }
            return deleteCount;
        }

        /// <summary>
        /// Deletes profile properties and information for profiles that match the supplied list of user names.
        /// </summary>
        /// <param name="usernames">A string array of user names for profiles to be deleted.</param>
        /// <returns>The number of profiles deleted from the data source.</returns>
        public override int DeleteProfiles(string[] usernames)
        {
            int deleteCount = 0;
            foreach (string username in usernames)
            {
                if (DeleteProfile(username))
                {
                    deleteCount++;
                }
            }
            return deleteCount;
        }

        //Check the username 
        private void CheckUserName(string userName)
        {
            if (string.IsNullOrEmpty(userName) || userName.Length > 256 || userName.IndexOf(",") > 0)
                throw new ApplicationException(ERR_INVALID_PARAMETER + " user name.");
        }

        /// <summary>
        /// Deletes all user-profile data for profiles in which the last activity date occurred before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are deleted.</param>
        /// <param name="userInactiveSinceDate">A System.DateTime that identifies which user profiles are considered inactive. If the System.Web.Profile.ProfileInfo.LastActivityDate value of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <returns>The number of profiles deleted from the data source.</returns>
        public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
        {
            string[] userArray = new string[0];
            dal.GetInactiveProfiles((int)authenticationOption, userInactiveSinceDate, ApplicationName).CopyTo(userArray, 0);

            return DeleteProfiles(userArray);
        }

        /// <summary>
        /// Retrieves profile information for profiles in which the user name matches the specified user names.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>A System.Web.Profile.ProfileInfoCollection containing user-profile information
        //     for profiles where the user name matches the supplied usernameToMatch parameter.</returns>
        public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            CheckParameters(pageIndex, pageSize);

            return GetProfileInfo(authenticationOption, usernameToMatch, null, pageIndex, pageSize, out totalRecords);
        }

        /// <summary>
        /// Retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <param name="userInactiveSinceDate">A System.DateTime that identifies which user profiles are considered inactive. If the System.Web.Profile.ProfileInfo.LastActivityDate value of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>A System.Web.Profile.ProfileInfoCollection containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter.</returns>	
        public override ProfileInfoCollection  FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
        {
            CheckParameters(pageIndex,pageSize);
            return GetProfileInfo(authenticationOption,usernameToMatch, userInactiveSinceDate, pageIndex, pageSize, out totalRecords);
        }

        /// <summary>
        /// Retrieves user profile data for all profiles in the data source.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>A System.Web.Profile.ProfileInfoCollection containing user-profile information for all profiles in the data source.</returns>	
        public override ProfileInfoCollection  GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
        {
            CheckParameters(pageIndex,pageSize);
            return GetProfileInfo(authenticationOption, null, null, pageIndex, pageSize, out totalRecords);
        }

        /// <summary>
        /// Retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="userInactiveSinceDate">A System.DateTime that identifies which user profiles are considered inactive. If the System.Web.Profile.ProfileInfo.LastActivityDate of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>A System.Web.Profile.ProfileInfoCollection containing user-profile information about the inactive profiles.</returns>
        public override ProfileInfoCollection  GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
        {
            CheckParameters(pageIndex, pageSize);
		    return GetProfileInfo(authenticationOption, null, userInactiveSinceDate, pageIndex, pageSize, out totalRecords);
        }
        
        /// <summary>
        /// Returns the number of profiles in which the last activity date occurred on or before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the System.Web.Profile.ProfileAuthenticationOption values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="userInactiveSinceDate">A System.DateTime that identifies which user profiles are considered inactive. If the System.Web.Profile.ProfileInfo.LastActivityDate of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <returns>The number of profiles in which the last activity date occurred on or before the specified date.</returns>
        public override int  GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
        {
 	
		    int inactiveProfiles = 0;
		    ProfileInfoCollection profiles = GetProfileInfo(authenticationOption, null, userInactiveSinceDate, 0, 0, out inactiveProfiles);
		    return inactiveProfiles;
        }

        //Verifies input parameters for page size and page index.
		private static void CheckParameters(int pageIndex, int pageSize) 
        {
			if(pageIndex < 1 || pageSize < 1)
            {
				throw new ApplicationException(ERR_INVALID_PARAMETER + " page index.");
            }
		}

		 //GetProfileInfo
		 //Retrieves a count of profiles and creates a 
		 //ProfileInfoCollection from the profile data in the 
		 //database. Called by GetAllProfiles, GetAllInactiveProfiles,
		 //FindProfilesByUserName, FindInactiveProfilesByUserName, 
		 //and GetNumberOfInactiveProfiles.
		 //Specifying a pageIndex of 0 retrieves a count of the results only.
		private static ProfileInfoCollection GetProfileInfo(ProfileAuthenticationOption authenticationOption, string usernameToMatch, object userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) 
        {
			
		    ProfileInfoCollection profiles = new ProfileInfoCollection();

			totalRecords = 0;

			// Count profiles only.
			if(pageSize == 0)
				return profiles;

			int counter = 0;
			int startIndex = pageSize * (pageIndex - 1);
			int endIndex = startIndex + pageSize - 1;

			DateTime dt = new DateTime(1900, 1, 1);
			if(userInactiveSinceDate != null)
            {
				dt = (DateTime)userInactiveSinceDate;
            }

			foreach(UserProfileInfo profile in dal.GetProfileInfo((int)authenticationOption, usernameToMatch, dt, applicationName, out totalRecords)) 
            {
		        if(counter >= startIndex) 
                {
		            ProfileInfo p = new ProfileInfo(profile.Username, profile.IsAnonymous, profile.LastUpdatedTime, profile.LastUpdatedTime, 0);
		            profiles.Add(p);
		        }

				if(counter >= endIndex)
                {
					break;
				}

				counter++;
		    }

		    return profiles;
		}
	}
}
